package objects;

import containers.Canvas;
import containers.Simulation;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;


/**
 * 
 * @author Robert C. Duvall. Edited by rwb10 & sab60
 */
public class Spring {
    private Mass myStart;
    private Mass myEnd;
    private double myLength;
    private double myK;

    /**
     * 
     * @param start The first Mass connected.
     * @param end The second Mass connected.
     * @param length Rest length of spring.
     * @param kVal Spring constant.
     */
    public Spring (Mass start, Mass end, double length, double kVal) {
        setMyStart(start);
        setMyEnd(end);
        setMyLength(length);
        myK = kVal;
    }

    /**
     * 
     * @param pen Pen with which to paint.
     */
    public void paint (Graphics2D pen) {

        // the color of the spring depends on whether
        // it is acting as a pushing or pulling force
        double len = Force.distanceBetween(getMyStart().getCenter(),
                getMyEnd().getCenter()) - getMyLength();

        Color color;
        if (Math.abs(len) < 0.001) {
            color = Color.WHITE;
        }
        else if (len < 0.0) {
            color = Color.BLUE;
        }
        else {
            color = Color.RED;
        }
        Canvas.paintLine(pen, getMyStart().getCenter(), getMyEnd().getCenter(), color);
    }

    /**
     * 
     * @param canvas Simulation spring is in.
     * @param dt Amount of time passed.
     */
    public void update (Simulation canvas, double dt) {
        // apply hooke's law to each attached mass
        Force f = hookesForce();
        applyScaledForce(getMyStart(), f);
        f.negate();
        applyScaledForce(getMyEnd(), f);
    }

    /**
     * returns the difference between the current length
     * and rest length of the spring.
     */
    protected double restDistance() {
        return getMyLength() - currentLength();
    }

    /**
     * returns the currentLength,
     * i.e. the distance between start and end masses.
     */
    protected double currentLength() {
        Point2D start = getMyStart().getCenter();
        Point2D end = getMyEnd().getCenter();
        return Force.distanceBetween(start, end);
    }

    /**
     * returns the angle between the start
     * and end masses.
     */
    protected double angle() {
        Point2D start = getMyStart().getCenter();
        Point2D end = getMyEnd().getCenter();
        return Force.angleBetween(start, end);
    }

    /**
     * returns the force resulting from Hooke's law
     * based on restDistance and myK
     */
    protected Force hookesForce() {
        return new Force(angle(), myK * (restDistance()));
    }

    /**
     * Applies a properly scaled "force" to a mass.
     * Actually takes forces and calculates the effective
     * acceleration to the masses.
     * 
     * @param mass The mass to which the force
     *        is scaled and applied
     * @param force The force that is being scaled.
     */
    public void applyScaledForce (Mass mass, Force force) {
        mass.applyForce(new Force(force.getDirection(), force.getMagnitude() / mass.getMass()));
    }

    /**
     * Returns true if the input massID matches myStart or myEnd.
     * 
     * @param massID The mass being searched for.
     * @return
     */
    public boolean containsMass (int massID) {
        return getMyStart().match(massID) || getMyEnd().match(massID);
    }

    /**
     * @return the myLength
     */
    public double getMyLength () {
        return myLength;
    }

    /**
     * @param mySpringLength the myLength to set
     */
    public void setMyLength (double mySpringLength) {
        this.myLength = mySpringLength;
    }

    /**
     * @return the myStart
     */
    public Mass getMyStart () {
        return myStart;
    }

    /**
     * @param myStartMass the myStart to set
     */
    public void setMyStart (Mass myStartMass) {
        this.myStart = myStartMass;
    }

    /**
     * @return the myEnd
     */
    public Mass getMyEnd () {
        return myEnd;
    }

    /**
     * @param myEndMass the myEnd to set
     */
    public void setMyEnd (Mass myEndMass) {
        this.myEnd = myEndMass;
    }
}
